package tech.yixiyun.framework.kuafu.plugins.hotdeploy;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.XmlUtil;
import tech.yixiyun.framework.kuafu.bean.annotation.Bean;
import tech.yixiyun.framework.kuafu.boot.bootedhandler.IBootedHandler;
import tech.yixiyun.framework.kuafu.config.AppConfig;
import tech.yixiyun.framework.kuafu.config.ConfigKey;
import tech.yixiyun.framework.kuafu.log.LOGGER;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import java.io.File;
import java.io.FileFilter;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 热加载插件，需要在system.run.hotdeploy中配置为true，并且安装jrebel插件使用。
 * 项目开启jrebel，然后通过jrebel启动。详细教程参考()
 */
@Bean(remark = "启动后，根据配置文件中的system.run.hotdeploy判断是否开启热加载。")
public class HotDeployBootedHandler implements IBootedHandler {


    @Override
    public void handle() {
        Boolean enable = AppConfig.getAsBoolean(ConfigKey.RUN_HOTDEPLOY);
        if (Objects.equals(enable, true)) {
            LOGGER.infoTitle("启动热加载中，请确认已安装Jrebel插件、在该项目中启动Jrebel、以及通过Jrebel运行该项目。详细教程请参考(http://www.baidu.com)");
            List<String> classPaths = getJrebelClassPath();
            //返回null，说明有问题，
            if (classPaths == null) return;

            if (classPaths.isEmpty()) {
                LOGGER.warn("热加载启动失败：rebel.xml中未配置classpath");
                return;
            }

            classPaths.forEach(path -> {
                File file = new File(path);
                if (file.exists() == false) {
                    LOGGER.warn("热加载启动失败：rebel.xml中配置的classpath【" + path +"】不存在，请检查。");
                    return;
                }
                monitor(file);
            });

        }
    }


    /**
     * 监听目录
     * @param file
     */
    private void monitor(File file) {
        FileAlterationObserver observer=new FileAlterationObserver(file.getPath(), new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return true;
            }
        });

        observer.addListener(new HotDeployListener(file.toURI().getPath()));
        //每三秒扫描一次
        FileAlterationMonitor monitor=new FileAlterationMonitor(800, observer);
        try {
            monitor.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
        LOGGER.info("热加载启动：开始监听【{}】",file.getPath());
    }


    private List<String> getJrebelClassPath() {
        URL jrebelConfig = ClassUtil.getClassLoader().getResource("rebel.xml");
        if (jrebelConfig == null) {
            LOGGER.warn("Jrebel的配置文件：rebel.xml未找到，开启热加载失败");
            return null;
        }
        LOGGER.debug("Jrebel的配置文件：rebel.xml已找到，尝试读取中...");
        Document document = XmlUtil.readXML(new File(jrebelConfig.getFile()));
        NodeList nodes = document.getElementsByTagName("application");
        List<String> classPaths = new ArrayList<>();
        try {
            nodes = nodes.item(0).getChildNodes();
            for (int i = 0; i < nodes.getLength(); i++) {
                Node item = nodes.item(i);
                if (item.getNodeName().equals("classpath")) {
                    NodeList childNodes = item.getChildNodes();
                    for (int j = 0; j < childNodes.getLength(); j++) {
                        Node child = childNodes.item(j);
                        if (child.getNodeName().equals("dir")) {
                            String path = child.getAttributes().getNamedItem("name").getNodeValue();
                            classPaths.add(path);
                        }
                    }
                }

            }
            return classPaths;
        }catch (Exception e) {
            LOGGER.error("热加载启动失败...", e);
            return null;
        }
    }
}
